package com.sunpy.niohttp.core;

import com.sunpy.niohttp.annotation.impl.RequestProxy;
import com.sunpy.niohttp.codec.HttpDecoder;
import com.sunpy.niohttp.codec.HttpEncoder;
import com.sunpy.niohttp.file.HttpPropertiesConfig;
import com.sunpy.niohttp.model.ConfigInfo;
import com.sunpy.niohttp.model.SunpyRequest;
import com.sunpy.niohttp.model.SunpyResponse;

import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;

public class HttpServer {

    public void start() {
        try {
            /**
             * 从nio-http.properties文件中读取配置属性
             */
            ConfigInfo configInfo = HttpPropertiesConfig.getConfig();

            doServer(configInfo.getServerIP(), configInfo.getServerPort());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    public void doServer(String ip, int port) throws Exception {
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        serverSocketChannel.configureBlocking(false);
        serverSocketChannel.bind(new InetSocketAddress(ip, port));
        Selector selector = Selector.open();
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
        System.out.println("-----------> 服务器启动成功");
        // 在selector上注册的键不为0
        while(selector.select() > 0) {
            Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();

            while (iterator.hasNext()) {
                SelectionKey selectionKey = iterator.next();

                // 准备好接收读写数据操作
                if (selectionKey.isValid() && selectionKey.isAcceptable()) {
                    ServerSocketChannel acceptSocketChannel = (ServerSocketChannel) selectionKey.channel();
                    // 接收与此套接字相关的连接，阻塞的
                    SocketChannel clientChannel = acceptSocketChannel.accept();
                    /**
                     * 前面接收数据请求是非阻塞的，但是接收数据的accept方法是阻塞的。
                     * 所以采用线程池来读写通道中的数据
                     */
                    if (clientChannel != null) {
                        // 客户端通道设置为非阻塞
                        clientChannel.configureBlocking(false);

                        // 该通道注册为读键
                        clientChannel.register(selector, SelectionKey.OP_READ);
                    }
                } else if (selectionKey.isValid() && selectionKey.isReadable()) {
                    SocketChannel clientChannel = (SocketChannel) selectionKey.channel();
                    ByteBuffer buffer = ByteBuffer.allocate(1024);
                    /**
                     * 1. 从通道中解析出http请求对象
                     */
                    // 将数据读到缓冲区中
                    clientChannel.read(buffer);
                    // 从缓冲区中存数据状态转换到从缓冲区中取数据状态
                    buffer.flip();
                    // 将缓冲区中数据解码成SunpyRequest对象
                    SunpyRequest sunpyRequest = new HttpDecoder().decodeHttp(buffer);
                    // 清空缓存区
                    buffer.clear();

                    /**
                     * 2. 查找请求的映射注解，委托代理执行业务逻辑方法
                     */
                    System.out.println(sunpyRequest);
                    SunpyResponse sunpyResponse = new SunpyResponse();
                    new RequestProxy(sunpyRequest, sunpyResponse).doServiceByProxy();
                    System.out.println(sunpyResponse);

                    /**
                     * 4. 将http协议的响应对象使用通道发送给客户端
                     */
                    // 将SunpyResponse对象编码成byte数组
                    byte[] response = new HttpEncoder().encodeHttp(sunpyResponse);
                    // 将byte字节数组存入到缓冲区中
                    buffer.put(response);
                    // 注册写事件
                    selectionKey.interestOps(SelectionKey.OP_WRITE);
                    // 从缓冲区中存的状态转换为从缓冲区中取的状态
                    buffer.flip();

                    // 只要缓冲区中position到limit之间的数据没写完，就一直往通道写入数据
                    while (buffer.hasRemaining()) {
                        clientChannel.write(buffer);
                    }

                    buffer.clear();
                    clientChannel.close();
                }
            }

            iterator.remove();
        }
    }

    public static void main(String[] args) {
        HttpServer server = new HttpServer();
        server.start();
    }
}
